package com.redis.common.component;

import com.redis.common.component.exception.CustomOAuth2Exception;
import com.redis.common.component.exception.UnauthorizedException;
import com.redis.common.util.SecurityMessageSourceUtil;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
import org.springframework.security.oauth2.common.exceptions.ClientAuthenticationException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.web.HttpRequestMethodNotSupportedException;

import java.util.Locale;

/**
 * @description: oauth2 认证服务异常处理,重写oauth2的默认实现
 * 异常处理 {@link org.springframework.security.core.AuthenticationException } 不同细化异常处理
 * @Author C_Y_J
 * @create 2022/1/12 9:48
 **/
public class CustomWebResponseExceptionTranslator implements WebResponseExceptionTranslator<OAuth2Exception> {

    private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();

    @Override
    public ResponseEntity<OAuth2Exception> translate(Exception e) {

        // Try to extract a SpringSecurityException from the stacktrace
        // 尝试从堆栈跟踪中提取 SpringSecurityException
        Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e);

        // 未经授权的异常
        Exception ase = (AuthenticationException) throwableAnalyzer
                .getFirstThrowableOfType(AuthenticationException.class, causeChain);
        if (ase != null) {
            return handleOAuth2Exception(new UnauthorizedException(ase.getMessage(), ase));
        }

        // 访问被拒绝异常
        ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
        if (ase != null) {
            return handleOAuth2Exception(new UnauthorizedException.ForbiddenException(ase.getMessage(), ase));
        }

        // InvalidGrantException
        ase = (InvalidGrantException) throwableAnalyzer.getFirstThrowableOfType(InvalidGrantException.class, causeChain);
        if (ase != null) {
            return handleOAuth2Exception(new UnauthorizedException.InvalidException(ase.getMessage(), ase));
        }

        // Http 请求方法不支持异常
        ase = (HttpRequestMethodNotSupportedException) throwableAnalyzer.getFirstThrowableOfType(HttpRequestMethodNotSupportedException.class, causeChain);
        if (ase != null) {
            return handleOAuth2Exception(new UnauthorizedException.MethodNotAllowedException(ase.getMessage(), ase));
        }

        // 处理不合法的令牌错误
        ase = (InvalidTokenException) throwableAnalyzer.getFirstThrowableOfType(InvalidTokenException.class, causeChain);
        if (ase != null) {
            return handleOAuth2Exception(new UnauthorizedException.TokenInvalidException(ase.getMessage(), ase));
        }

        ase = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType(OAuth2Exception.class, causeChain);
        if (ase != null) {
            return handleOAuth2Exception((OAuth2Exception) ase);
        }

        // 这个最后一个异常，默认为服务器挂了
        String reasonPhrase = HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
        return handleOAuth2Exception(new UnauthorizedException.ServerErrorException(reasonPhrase, e));

    }

    private ResponseEntity<OAuth2Exception> handleOAuth2Exception(OAuth2Exception e) {
        int status = e.getHttpErrorCode();
        // 客户端异常直接返回客户端,不然无法解析
        if (e instanceof ClientAuthenticationException) {
            return new ResponseEntity<>(e, HttpStatus.valueOf(status));
        }

        CustomOAuth2Exception exception = new CustomOAuth2Exception(e.getMessage(), e.getOAuth2ErrorCode());
        return new ResponseEntity<>(exception, HttpStatus.valueOf(status));

    }

}
